using System;
using System.Collections.Generic;
using System.Text;

using System.Windows.Forms;

// thanks to this open source component, it's saving us a lot of time and effort...
using zlib;

namespace Chanutils
{
    public static class HELP
    {
        /// <summary>
        /// Dumps an exception to disk, and summarises the exception to the user, questioning if they want
        /// to continue running the application or not.
        /// </summary>
        /// <param name="ex">The unwanted exception</param>
        public static void DumpException(Exception ex)
        {
            DumpException(true, ex);
        }

        /// <summary>
        /// Dumps an exception to disk, optionally complaining to the user about it (asking them if they wish
        /// to continue executing).
        /// </summary>
        /// <param name="ComplainToUser">If true, displays a message box to the user containing summarised info about the exception.</param>
        /// <param name="ex">The exception in question</param>
        public static void DumpException(bool ComplainToUser, Exception ex)
        {
            string sFilename = GetDebugDumpDirectory() + "/" + GetUniqueDateTimeString() + ".exception.txt";

            System.IO.StreamWriter sw = System.IO.File.CreateText(sFilename);

            sw.WriteLine("Exception occurred!");
            sw.WriteLine("  Message: " + ex.Message);
            sw.WriteLine("  Source: " + ex.Source);

            sw.WriteLine("  Data[] strings: ");
            System.Collections.IDictionaryEnumerator Ide = ex.Data.GetEnumerator();
            for (int iC = 1; iC <= ex.Data.Count; iC++)
            {
                if (iC != 1)
                    Ide.MoveNext();

                sw.WriteLine("    Key: '" + Ide.Entry.Key.ToString() + "'\r\n      Value: '" + Ide.Entry.Value.ToString() + "'");
            }

            sw.WriteLine("  HelpLink: " + ex.HelpLink);
            sw.WriteLine("  TargetSite: (Module.ScopeName: '" + ex.TargetSite.Module.ScopeName + "') (Name: '" + ex.TargetSite.Name + "')");

            sw.WriteLine("  StackTrace: " + ex.StackTrace);

            sw.WriteLine("  ComplainToUser: '" + ComplainToUser.ToString() + "'");

            sw.Flush();
            sw.Close();
            sw = null;

            if (ComplainToUser)
            {
                DialogResult dr = MessageBox.Show("Exception occured: '" + ex.Message + "' (by '" + ex.Source + "'). \r\n\r\nMore information has been dumped into " + sFilename + ", examine this file if you want to know more.\r\n\r\nContinue executing this process?", "Chan-Cli Exception", MessageBoxButtons.YesNo, MessageBoxIcon.Error);

                switch (dr)
                {
                    case DialogResult.Yes:
                        // Do nothing
                        break;
                    case DialogResult.No:
                        Application.Exit();
                        break;
                }
            }
        }

        /// <summary>
        /// Represents a directory that you can dump debugging crap to.  By calling this function, you create
        /// it if it doesn't already exist! CALL IT ONLY WHEN YOU NEED IT
        /// </summary>
        /// <returns>A string representing the directory you can dump to. No \ or / on the end</returns>
        public static string GetDebugDumpDirectory()
        {
            if (!System.IO.Directory.Exists(System.Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory) + "/Chan-Cli Debug"))
            {
                try
                {
                    System.IO.Directory.CreateDirectory(System.Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory) + "/Chan-Cli Debug");
                }
                catch (Exception e)
                {
                    MessageBox.Show("Cannot create chan-cli debug directory;  Please make sure the desktop is writeable to!\r\n\r\nWithout this, the program is unable to present the debugging information for an error that just occured and will now quit.\r\nThe desktop directory is listed as being: '" + System.Environment.SpecialFolder.DesktopDirectory + "'.\r\nThe intended name for the debugging directory to be created is 'Chan-Cli Debug'.\r\n\r\nException details: '" + e.Message + "'", "Fatal!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    Application.Exit();
                }
            }

            return System.Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory) + "/Chan-Cli Debug";
        }

        /// <summary>
        /// Returns a string of the format: shortdate shorttime (ticks)
        /// </summary>
        /// <returns>See summary, fo shizzle.</returns>
        public static string GetUniqueDateTimeString()
        {
            return System.DateTime.Now.ToShortDateString().Replace('/', '.') + " " + System.DateTime.Now.ToShortTimeString().Replace(':', '.') + " " + "(" + System.DateTime.Now.Ticks + ")";
        }

        /// <summary>
        /// Uses the zlib.net component to compress the provided byte[] buffer, and returns the result.
        /// </summary>
        /// <param name="InputBuffer">Buffer of bytes to be compressed with zlib.net</param>
        /// <returns>Buffer of bytes, that consists of InputBuffer after being fed to zlib.net's 'Deflate' algorithm</returns>
        public static byte[] ZlibEncode(byte[] InputBuffer)
        {
            return ZlibOperation(true, InputBuffer);
        }

        /// <summary>
        /// Uses the zlib.net component to decompress the provided byte[] buffer, and returns what the data would have been
        /// _before_ encoding.
        /// </summary>
        /// <param name="CompressedInputBuffer">Buffer of bytes to be decompressed (with zlib.net)</param>
        /// <returns>Buffer of bytes, consisting of what CompressedInputBuffer was before compression</returns>
        public static byte[] ZlibDecode(byte[] CompressedInputBuffer)
        {
            return ZlibOperation(false, CompressedInputBuffer);            
        }

        /// <summary>
        /// Used internally by ZlibEncode and ZlibDecode; USE THOSE INSTEAD!
        /// </summary>
        /// <param name="CompressMode">'true' puts us in Encode mode, otherwise Decode</param>
        /// <param name="InputBuffer">Stuff to be fed to zlib.net</param>
        /// <returns>Buffer of bytes, return data and..shit.</returns>
        private static byte[] ZlibOperation(bool CompressMode, byte[] InputBuffer)
        {
            byte[] xOutBuffer;
            System.IO.MemoryStream mStream = new System.IO.MemoryStream();

            ZOutputStream zOut;

            //
            // TODO: BENCHMARK THIS to see how worse/better 'Z_BEST_COMPRESSION' and the default settings really are..
            //
            // GOD THEY REALLY DON'T MAKE THIS CLEAR
            //  Essentially, the second parameter is the difference between a 'deflate' stream,
            //   and an 'inflate' stream... (If provided then compress, otherwise decompress obviously)
            if (CompressMode)
                zOut = new ZOutputStream(mStream, zlib.zlibConst.Z_BEST_SPEED);
            else
                zOut = new ZOutputStream(mStream);

            // Encode...
            zOut.Write(InputBuffer, 0, InputBuffer.Length);
            zOut.Close();
            zOut = null;

            // ... Now, let's get our data back ...
            xOutBuffer = new byte[mStream.Length];
            new System.IO.BinaryReader(mStream).Read(xOutBuffer, 0, (int) mStream.Length);

            // clean-up a little..
            mStream.Close();
            mStream = null;

            return xOutBuffer;
        }
    }
}
